﻿// =====================================================================
//  File:		IsvConfigCustomizer.cs
//  Summary:	[FILE DESCRIPTION GOES HERE]
// =====================================================================
//
//  This file is part of the Microsoft CRM V3.0 SDK Code Samples.
//
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//  This source code is intended only as a supplement to Microsoft
//  Development Tools and/or on-line documentation.  See these other
//  materials for detailed information regarding Microsoft code samples.
//
//  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//
// =====================================================================

using System;
using System.Xml;
using System.Globalization;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Text;
using System.IO;
using System.Reflection;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;
//using Microsoft.CrmSdk.Discovery;
using Microsoft.Win32;
using System.Collections;
using System.Net;


namespace SharpTeam.Crm.IsvCustomization
{
    #region Enums
    public enum AvailableOfflineType
    {
        None,
        [DescriptionAttribute("true")]
        True,
        [DescriptionAttribute("false")]
        False
    }
    public enum IsProfileType
    {
        None,
        [DescriptionAttribute("true")]
        True,
        [DescriptionAttribute("false")]
        False
    }
    public enum ShowGroupsType
    {
        None,
        [DescriptionAttribute("true")]
        True,
        [DescriptionAttribute("false")]
        False
    }
    [Flags]
    public enum LicenseTypes
    {
        None = 0,
        Professional = 1,
        SmallBusiness = 2,
        All = Professional | SmallBusiness
    }
    [Flags]
    public enum PrivilegeTypes
    {
        None = 0,
        Append = 1,
        AppendTo = 2,
        Assign = 4,
        Create = 8,
        Delete = 16,
        Read = 32,
        Share = 64,
        Write = 128,
        All = Append | AppendTo | Assign | Create | Delete | Read | Share | Write
    }
    public enum ConfigurationBoolean
    {
        Undefined,
        [DescriptionAttribute("true")]
        True,
        [DescriptionAttribute("false")]
        False
    }
    public enum WindowMode
    {
        Undefined = 0,
        [DescriptionAttribute("0")]
        Window = 1,
        [DescriptionAttribute("1")]
        ModalDialog = 2,
        [DescriptionAttribute("2")]
        ModelessDialog = 3
    }
    [Flags]
    public enum ClientTypes
    {
        Undefined = -1,
        None = 0,
        Outlook = 1,
        OutlookLaptopClient = 2,
        OutlookWorkstationClient = 4,
        Web = 8,
        All = Outlook | OutlookLaptopClient | OutlookWorkstationClient | Web
    }
    #endregion


    #region IsvConfigCustomizer
    public class IsvConfigCustomizer
    {
        private isvconfig isvConf = null;
        private CrmService service = new CrmService();
        private CrmAuthenticationToken token = new CrmAuthenticationToken();
        private XmlDocument _importExportXml = new XmlDocument();
        private XmlDocument _isvConfigXml;

        public ArrayList ErrorMessages = new ArrayList();
        public int LCID = 1033;
        // TODO Different languages

        /// <summary>
        /// Returns the Import Export XML document
        /// </summary>
        public XmlDocument Xml
        {
            get
            {
                XmlNode child = _importExportXml.SelectSingleNode("/ImportExportXml/IsvConfig");
                XmlNode parent = _importExportXml.SelectSingleNode("/ImportExportXml");

                XmlCDataSection data = _importExportXml.CreateCDataSection(_isvConfigXml.OuterXml);
                XmlNode newChild = _importExportXml.CreateElement("IsvConfig");
                newChild.AppendChild(data);

                parent.ReplaceChild(newChild, child);	// assumes that IsvConfig node exists

                return _importExportXml;
            }
        }

        #region Constructors
        //public IsvConfigCustomizer()
        //{
        //    try
        //    {
        //        string url = (string)Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\MSCRM").GetValue("ServerUrl", string.Empty);
        //        CrmDiscoveryService dService = new CrmDiscoveryService();
        //        dService.UseDefaultCredentials = true;
        //        dService.Url = url + "/2007/AD/CrmDiscoveryService.asmx";
        //        RetrieveOrganizationsRequest req = new RetrieveOrganizationsRequest();
        //        RetrieveOrganizationsResponse resp = (RetrieveOrganizationsResponse)dService.Execute(req);
        //        foreach (OrganizationDetail orgDet in resp.OrganizationDetails)
        //        {
        //            InitializeService(orgDet.OrganizationName, url);
        //            break;
        //        }
        //    }
        //    catch (System.Web.Services.Protocols.SoapException err)
        //    {
        //        ErrorMessages.Add(err.Message + "\n" + err.Detail.InnerText);
        //    }
        //    catch (Exception err)
        //    {
        //        ErrorMessages.Add(err.Message + "\n" + err.StackTrace);
        //    }
        //}

        public IsvConfigCustomizer(string orgName)
        {
            try
            {
                string url = (string)Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\MSCRM").GetValue("ServerUrl", string.Empty);
                InitializeService(orgName, url);
            }
            catch (System.Web.Services.Protocols.SoapException err)
            {
                ErrorMessages.Add(err.Message + "\n" + err.Detail.InnerText);
            }
            catch (Exception err)
            {
                ErrorMessages.Add(err.Message + "\n" + err.StackTrace);
            }
        }

        public IsvConfigCustomizer(string orgName, string serverUrl)
        {
            InitializeService(orgName, serverUrl + (serverUrl.EndsWith("/") ? "mscrmservices" : "/mscrmservices"));
        }

        public IsvConfigCustomizer(string orgName, string serverUrl, ICredentials credentials)
        {
            InitializeService(orgName, serverUrl + (serverUrl.EndsWith("/") ? "mscrmservices" : "/mscrmservices"), credentials);
        }

        public IsvConfigCustomizer(string orgName, string serverUrl, string username, string password, string domain)
        {
            InitializeService(orgName, serverUrl + (serverUrl.EndsWith("/") ? "mscrmservices" : "/mscrmservices"), username, password, domain);
        }
        #endregion

        #region Global Menu Bar / ToolBar Methods

        /// <summary>
        /// Add a new button to the global toolbar
        /// </summary>
        /// <param name="button">The button to add</param>
        public void AddGlobalToolBarButton(ToolBarButton button)
        {
            // Get the global toolbar and ensure it exists
            XmlNode toolBar = this.GetGlobalToolBar();

            // Add the button
            this.AddButtonToToolBar(toolBar, button);
        }

        /// <summary>
        /// Add a new "spacer" to the global tool bar
        /// </summary>
        public void AddGlobalToolBarSpacer()
        {
            // Get the global toolbar and ensure it exists
            XmlNode toolBar = this.GetGlobalToolBar();

            // Add the spacer
            this.AddSpacerToToolBar(toolBar);
        }

        /// <summary>
        /// Add a new Menu & Menu Item to the global menu bar.  If the specified menu already exists, the item will be
        /// added to it.  If the specified menu does not exist, the menu will be added and then the item will be added.
        /// </summary>
        /// <param name="menuTitle">The display title of the menu - This is used as both the user viewable title of the
        /// menu and the key by which menu items are added to menus.</param>
        /// <param name="menuItem">The MenuItem to add</param>
        public void AddGlobalMenuItem(string menuTitle, MenuItem menuItem)
        {
            // Get the global menu bar and ensure it exists
            XmlNode menuBar = this.GetGlobalMenuBar();

            // Add the menu item
            this.AddMenuItemToMenuBarMenu(menuBar, menuTitle, menuItem);
        }

        /// <summary>
        /// Add a new Menu & Spacer to the global menu bar.  If the specified menu already exists, the Spacer will be
        /// added to it.  If the specified menu does not exist, the menu will be added and then the Spacer will be added.
        /// </summary>
        /// <param name="menuTitle">The display title of the menu - This is used as both the user viewable title of the
        /// menu and the key by which spacers are added to menus.</param>
        public void AddGlobalMenuSpacer(string menuTitle)
        {
            // Get the global menu bar and ensure it exists
            XmlNode menuBar = this.GetGlobalMenuBar();

            // Add the spacer
            this.AddMenuSpacerToMenuBarMenu(menuBar, menuTitle);
        }

        #endregion

        #region Grid Menu / ToolBar Methods

        /// <summary>
        /// Add a new button to the grid toolbar for an entity
        /// </summary>
        /// <param name="entityName">The name of the entity whose grid toolbar you would like to modify</param>
        /// <param name="button">The button to add</param>
        public void AddEntityGridToolBarButton(string entityName, ToolBarButton button)
        {
            // Get the entity grid toolbar and ensure it exists
            XmlNode toolBar = this.GetEntityGridToolBar(entityName);

            // Add the button
            this.AddButtonToToolBar(toolBar, button);
        }

        /// <summary>
        /// Add a new "spacer" to the grid toolbar for an entity
        /// </summary>
        /// <param name="entityName">The name of the entity whose grid toolbar you would like to modify</param>
        public void AddEntityGridToolBarSpacer(string entityName)
        {
            // Get the entity grid toolbar and ensure it exists
            XmlNode toolBar = this.GetEntityGridToolBar(entityName);

            // Add the spacer
            this.AddSpacerToToolBar(toolBar);
        }

        /// <summary>
        /// Add a new Menu Item to the Actions Menu of an entity grid.
        /// </summary>
        /// <param name="entityName">The name of the entity whose grid Actions Menu you would like to modify</param>
        /// <param name="menuItem">The MenuItem to add</param>
        public void AddEntityGridActionMenuItem(string entityName, MenuItem menuItem)
        {
            // Get the entity grid actions menu and ensure it exists
            XmlNode menuBar = this.GetEntityActionsMenuBar(entityName);

            // Add the menu item
            this.AddMenuItemToMenuBarMenu(menuBar, null, menuItem);
        }

        /// <summary>
        /// Add a new Spacer to the Actions Menu of an entity grid.
        /// </summary>
        /// <param name="entityName">The name of the entity whose grid Actions Menu you would like to modify</param>
        public void AddEntityGridActionMenuSpacer(string entityName)
        {
            // Get the entity grid actions menu and ensure it exists
            XmlNode menuBar = this.GetEntityActionsMenuBar(entityName);

            // Add the spacer
            this.AddMenuSpacerToMenuBarMenu(menuBar, null);
        }

        #endregion

        #region Entity Menu / Tool Bar Methods

        /// <summary>
        /// Add a new button to the detail form toolbar of an entity
        /// </summary>
        /// <param name="entityName">The name of the entity whose toolbar you would like to modify</param>
        /// <param name="button">The button to add</param>
        public void AddEntityToolBarButton(string entityName, ToolBarButton button)
        {
            // Get the entity's toolbar and ensure it exists
            XmlNode toolBar = this.GetEntityToolBar(entityName);

            // Add the button
            this.AddButtonToToolBar(toolBar, button);
        }

        public void RemoveEntityToolBarButton(string entityName, string componentName)
        {
            // Get the entity's toolbar and ensure it exists
            XmlNode toolBar = this.GetEntityToolBar(entityName);

            // Add the button
            this.RemoveButtonFromToolBar(toolBar, componentName);
        }

        /// <summary>
        /// Add a new "spacer" to the detail form toolbar of an entity
        /// </summary>
        /// <param name="entityName">The name of the entity whose toolbar you would like to modify</param>
        public void AddEntityToolBarSpacer(string entityName)
        {
            // Get the entity's toolbar and ensure it exists
            XmlNode toolBar = this.GetEntityToolBar(entityName);

            // Add the spacer
            this.AddSpacerToToolBar(toolBar);
        }

        /// <summary>
        /// Add a new "Left Navigation Item" to the detail form of an entity
        /// </summary>
        /// <param name="entityName">The name of the entity whose left navbar you would like to modify</param>
        /// <param name="navItem">The LeftNavigationItem to add</param>
        public void AddEntityNavigationBarItem(string entityName, NavigationBarItem navItem)
        {
            // Get the entity's left navbar and ensure that it exists
            XmlNode navBar = this.GetEntityNavBar(entityName);

            // Если элемент с таким названием уже существует то не добавляем его повторно
            foreach (XmlNode nodeNavBar in navBar.ChildNodes)
            {
                foreach (XmlNode nodeTitles in nodeNavBar.ChildNodes)
                {
                    foreach (XmlNode nodeTitle in nodeTitles.ChildNodes)
                    {
                        if (nodeTitle.Attributes["Text"] != null && nodeTitle.Attributes["Text"].Value == navItem.Title) return;
                    }
                }
            }
            // Create the left navigation item xml
            XmlElement newElement = _isvConfigXml.CreateElement("NavBarItem");
            XmlElement titlesElement = _isvConfigXml.CreateElement("Titles");
            XmlElement titleElement = _isvConfigXml.CreateElement("Title");

            titleElement.SetAttribute("LCID", LCID.ToString());
            titleElement.SetAttribute("Text", navItem.Title);

            newElement.AppendChild(titlesElement);
            titlesElement.AppendChild(titleElement);

            newElement.SetAttribute("Icon", navItem.Icon);
            newElement.SetAttribute("Url", navItem.Url);
            newElement.SetAttribute("Id", navItem.Id);
            newElement.SetAttribute("PassParams", "1");
            newElement.SetAttribute("Area", navItem.Area);

            // Add the element to the navbar
            navBar.AppendChild(newElement);
        }

        /// <summary>
        /// Add a new Menu & Menu Item to the menu bar on the detail form of an entity.  If the specified menu already
        /// exists, the item will be added to it.  If the specified menu does not exist, the menu will be added and
        /// then the item will be added.
        /// </summary>
        /// <param name="entityName">The name of the entity whose menu bar you would like to modify</param>
        /// <param name="menuTitle">The display title of the menu - This is used as both the user viewable title of the
        /// menu and the key by which menu items are added to menus.</param>
        /// <param name="menuItem">The MenuItem to add</param>
        public void AddEntityMenuItem(string entityName, string menuTitle, MenuItem menuItem)
        {
            // Get the entity's menu bar and ensure that it exists
            XmlNode menuBar = this.GetEntityMenuBar(entityName);

            // Add the menu item
            this.AddMenuItemToMenuBarMenu(menuBar, menuTitle, menuItem);
        }

        /// <summary>
        /// Add a new Menu & Spacer to the menu bar on the detail form of an entity.  If the specified menu already
        /// exists, the spacer will be added to it.  If the specified menu does not exist, the menu will be added and
        /// then the spacer will be added.
        /// </summary>
        /// <param name="entityName">The name of the entity whose menu bar you would like to modify</param>
        /// <param name="menuTitle">The display title of the menu - This is used as both the user viewable title of the
        /// menu and the key by which menu items are added to menus.</param>
        public void AddEntityMenuSpacer(string entityName, string menuTitle)
        {
            // Get the entity's menu bar and ensure that it exists
            XmlNode menuBar = this.GetEntityMenuBar(entityName);

            // Add the spacer
            this.AddMenuSpacerToMenuBarMenu(menuBar, menuTitle);
        }

        #endregion

        #region Private Methods

        #region Add Helper Methods

        /// <summary>
        /// Adds a button to a toolbar
        /// </summary>
        /// <param name="toolBar">The toolbar node to add the button to</param>
        /// <param name="button">The button to add</param>
        private void AddButtonToToolBar(XmlNode toolBar, ToolBarButton button)
        {
            // Create the button
            XmlElement buttonElement = _isvConfigXml.CreateElement("Button");

            // "Hydrate" it...
            //AddOptionalAttributeToElement(buttonElement, "Title", button.Title);
            XmlElement titlesElement = _isvConfigXml.CreateElement("Titles");
            XmlElement titleElement = _isvConfigXml.CreateElement("Title");

            titleElement.SetAttribute("LCID", LCID.ToString());
            titleElement.SetAttribute("Text", button.Title);

            buttonElement.AppendChild(titlesElement);
            titlesElement.AppendChild(titleElement);


            XmlElement tooltipsElement = _isvConfigXml.CreateElement("ToolTips");
            XmlElement tooltipElement = _isvConfigXml.CreateElement("ToolTip");

            tooltipElement.SetAttribute("LCID",LCID.ToString());
            tooltipElement.SetAttribute("Text",button.ToolTip);

            buttonElement.AppendChild(tooltipsElement);
            tooltipsElement.AppendChild(tooltipElement);

            //AddOptionalAttributeToElement(buttonElement, "AccessKey", button.AccessHotKey.ToString(CultureInfo.InvariantCulture));
            AddOptionalAttributeToElement(buttonElement, "Icon", button.Icon);

            if (button.Url != null && button.Url.Length > 0 &&
                button.JScript != null && button.JScript.Length > 0)
            {
                throw new System.ArgumentException("Url and JScript are mutually exclusive - Only one may be set for a button.");
            }

            AddOptionalAttributeToElement(buttonElement, "Url", button.Url);
            AddOptionalAttributeToElement(buttonElement, "JScript", button.JScript);
            AddOptionalAttributeToElement(buttonElement, "WinMode", Convert.ToInt32(button.WindowLaunchMode).ToString());
            AddOptionalAttributeToElement(buttonElement, "Client", button.SupportedClients);
            AddOptionalAttributeToElement(buttonElement, "PassParams", Convert.ToInt32(button.PassParameters).ToString());
            AddOptionalAttributeToElement(buttonElement, "WinParams", button.WindowParameters);
            //AddOptionalAttributeToElement(buttonElement, "AvailableOffline", button.PassParameters.ToString().ToLower());
            //AddOptionalAttributeToElement(buttonElement, "ValidForCreate", Convert.ToInt32(button.PassParameters).ToString());
            //AddOptionalAttributeToElement(buttonElement, "ValidForUpdate", Convert.ToInt32(button.PassParameters).ToString());

            toolBar.AppendChild(buttonElement);
        }

        private void RemoveButtonFromToolBar(XmlNode toolBar, string componentName)
        {
            foreach (XmlNode node in toolBar.ChildNodes)
            {
                foreach (XmlNode toolTip in node.ChildNodes)
                {
                    if (toolTip.Name == "ToolTips")
                    {
                        if (toolTip.ChildNodes[0].Attributes["Text"] != null && toolTip.ChildNodes[0].Attributes["Text"].Value.Contains(componentName))
                        {
                            node.ParentNode.RemoveChild(node);
                        }
                    }
                }
            }
        }

        private void AddRequiredAttributeToElement(XmlElement element, string name, string value)
        {
            // Make sure the value is set, if not we will throw
            if (value != null && value.Length > 0)
            {
                throw new System.ArgumentNullException(name, "This property is required by the ISV.Config Schema");
            }
            else // It looks good enough, add it...
            {
                element.SetAttribute(name, value);
            }
        }

        private void AddOptionalAttributeToElement(XmlElement element, string name, string value)
        {
            // If the value hasn't been set, don't add an empty attribute
            if (value != null && value.Length > 0)
            {
                element.SetAttribute(name, value);
            }
        }

        private void AddOptionalAttributeToElement(XmlElement element, string name, ConfigurationBoolean value)
        {
            // If the value hasn't been set, don't add an empty attribute
            if (value != ConfigurationBoolean.Undefined)
            {
                // TODO: Get the correct ToString here
                element.SetAttribute(name, value.ToString());
            }
        }

        private void AddOptionalAttributeToElement(XmlElement element, string name, WindowMode value)
        {
            // If the value hasn't been set, don't add an empty attribute
            if (value != WindowMode.Undefined)
            {
                // TODO: Get the correct ToString here
                element.SetAttribute(name, value.ToString());
            }
        }

        private void AddOptionalAttributeToElement(XmlElement element, string name, ClientTypes value)
        {
            // If the value hasn't been set, don't add an empty attribute
            if (value != ClientTypes.Undefined)
            {
                // TODO: Implement the | (or'ing)
            }
        }





        /// <summary>
        /// Adds a spacer to a toolbar
        /// </summary>
        /// <param name="toolBar">The toolbar node to add the spacer to</param>
        private void AddSpacerToToolBar(XmlNode toolBar)
        {
            XmlElement newElement = _isvConfigXml.CreateElement("ToolBarSpacer");
            toolBar.AppendChild(newElement);
        }

        private void AddMenuItemToMenuBarMenu(XmlNode menuBar, string menuTitle, MenuItem menuItem)
        {
            XmlNode tmpNode;

            // This is an "Action Menu Item", add directly to it...
            if (menuTitle == null)
            {
                tmpNode = menuBar;
            }
            else
            {
                tmpNode = this.AddMenuToMenuBar(menuBar, menuTitle);
            }

            XmlElement newItem = _isvConfigXml.CreateElement("MenuItem");
            //newItem.SetAttribute("Title", menuItem.Title);
            XmlElement titlesElement = _isvConfigXml.CreateElement("Titles");
            XmlElement titleElement = _isvConfigXml.CreateElement("Title");

            titleElement.SetAttribute("LCID", LCID.ToString());
            titleElement.SetAttribute("Text", menuItem.Title);

            newItem.AppendChild(titlesElement);
            titlesElement.AppendChild(titleElement);


            tmpNode.AppendChild(newItem);
        }

        private void AddMenuSpacerToMenuBarMenu(XmlNode menuBar, string menuTitle)
        {
            XmlNode tmpNode;

            // This is an "Action Menu Item", add directly to it...
            if (menuTitle == null)
            {
                tmpNode = menuBar;
            }
            else
            {
                tmpNode = this.AddMenuToMenuBar(menuBar, menuTitle);
            }

            XmlElement newItem = _isvConfigXml.CreateElement("MenuSpacer");

            tmpNode.AppendChild(newItem);
        }

        private XmlNode AddMenuToMenuBar(XmlNode menuBar, string menuTitle)
        {
            XmlNode tmpNode = menuBar.SelectSingleNode("Menu/Titles/Title[@Text='" + menuTitle + "']");

            if (tmpNode == null)
            {
                XmlElement newElement = _isvConfigXml.CreateElement("Menu");

                XmlElement titlesElement = _isvConfigXml.CreateElement("Titles");
                XmlElement titleElement = _isvConfigXml.CreateElement("Title");

                titleElement.SetAttribute("LCID", LCID.ToString());
                titleElement.SetAttribute("Text", menuTitle);

                newElement.AppendChild(titlesElement);
                titlesElement.AppendChild(titleElement);

                //newElement.SetAttribute("Title", menuTitle);

                menuBar.AppendChild(newElement);

                tmpNode = menuBar.SelectSingleNode("Menu/Titles/Title[@Text='" + menuTitle + "']");
            }

            return tmpNode;
        }

        #endregion

        #region Node Helpers - Useful methods to find/create various nodes in the ISV.Config Schema

        private XmlNode GetGlobalToolBar()
        {
            XmlNode root = GetRootNode();

            XmlNode tmpNode = root.SelectSingleNode("ToolBar");

            if (tmpNode == null)
            {
                XmlElement newElement = _isvConfigXml.CreateElement("ToolBar");

                root.AppendChild(newElement);

                tmpNode = root.SelectSingleNode("ToolBar");
            }

            return tmpNode;
        }

        private XmlNode GetGlobalMenuBar()
        {
            XmlNode root = this.GetRootNode();
            return this.GetMenuBar(root);
        }

        private XmlNode GetEntityToolBar(string entityName)
        {
            XmlNode root = this.GetEntityNode(entityName);

            XmlNode tmpNode = root.SelectSingleNode("ToolBar");

            if (tmpNode == null)
            {
                XmlElement newElement = _isvConfigXml.CreateElement("ToolBar");
                root.AppendChild(newElement);
                tmpNode = root.SelectSingleNode("ToolBar");
            }

            return tmpNode;
        }

        private XmlNode GetEntityGridToolBar(string entityName)
        {
            XmlNode root = this.GetEntityGridNode(entityName);

            XmlNode tmpNode = root.SelectSingleNode("MenuBar");

            if (tmpNode == null)
            {
                XmlElement newElement = _isvConfigXml.CreateElement("MenuBar");
                root.AppendChild(newElement);

                tmpNode = root.SelectSingleNode("MenuBar");
            }

            XmlNode buttonsNode = tmpNode.SelectSingleNode("Buttons");

            if (buttonsNode == null)
            {
                XmlElement newElement = _isvConfigXml.CreateElement("Buttons");
                tmpNode.AppendChild(newElement);
                buttonsNode = tmpNode.SelectSingleNode("Buttons");
            }

            return buttonsNode;
        }

        private XmlNode GetEntityMenuBar(string entityName)
        {
            XmlNode root = this.GetEntityNode(entityName);
            return this.GetMenuBar(root);
        }

        private XmlNode GetEntityActionsMenuBar(string entityName)
        {
            XmlNode tmpNode = this.GetEntityGridNode(entityName);

            return this.GetActionMenuBar(tmpNode);
        }

        private XmlNode GetEntityGridNode(string entityName)
        {
            XmlNode root = this.GetEntityNode(entityName);
            XmlNode tmpNode = root.SelectSingleNode("Grid");

            if (tmpNode == null)
            {
                XmlElement newElement = _isvConfigXml.CreateElement("Grid");
                root.AppendChild(newElement);
                tmpNode = root.SelectSingleNode("Grid");
            }

            return tmpNode;
        }

        private XmlNode GetEntityNavBar(string entityName)
        {
            XmlNode root = this.GetEntityNode(entityName);
            XmlNode tmpNode = root.SelectSingleNode("NavBar");

            if (tmpNode == null)
            {
                XmlElement newElement = _isvConfigXml.CreateElement("NavBar");
                root.AppendChild(newElement);
                tmpNode = root.SelectSingleNode("NavBar");
            }

            return tmpNode;
        }

        private XmlNode GetMenuBar(XmlNode root)
        {
            return this.GetMenuContainerNode(root, "CustomMenus");
        }

        private XmlNode GetActionMenuBar(XmlNode root)
        {
            return this.GetMenuContainerNode(root, "ActionsMenu");
        }

        private XmlNode GetMenuContainerNode(XmlNode root, string containerName)
        {
            XmlNode tmpNode = root.SelectSingleNode("MenuBar/" + containerName);

            if (tmpNode == null)
            {
                tmpNode = root.SelectSingleNode("MenuBar");

                if (tmpNode == null)
                {
                    XmlElement newElement = _isvConfigXml.CreateElement("MenuBar");

                    root.AppendChild(newElement);
                }

                XmlElement newMenu = _isvConfigXml.CreateElement(containerName);

                tmpNode = root.SelectSingleNode("MenuBar");
                tmpNode.AppendChild(newMenu);

                tmpNode = root.SelectSingleNode("MenuBar/" + containerName);
            }

            return tmpNode;
        }

        private XmlNode GetConfigurationNode()
        {
                XmlNode tmpNode = _isvConfigXml.SelectSingleNode("/configuration");

                if (tmpNode == null)
                {
                    XmlElement newElement = _isvConfigXml.CreateElement("configuration");
                    newElement.SetAttribute("version", "4.0.0000.0");

                    _isvConfigXml.AppendChild(newElement);

                    tmpNode = _isvConfigXml.SelectSingleNode("/configuration");
                }

                return tmpNode;
        }

        private XmlNode GetRootNode()
        {
            XmlNode docRoot = GetConfigurationNode();

            XmlNode tmpNode = docRoot.SelectSingleNode("Root");

            if (tmpNode == null)
            {
                XmlElement newElement = _isvConfigXml.CreateElement("Root");

                docRoot.AppendChild(newElement);

                tmpNode = docRoot.SelectSingleNode("Root");
            }

            return tmpNode;
        }

        private XmlNode GetEntityNode(string entityName)
        {
            XmlNode docRoot = GetConfigurationNode().SelectSingleNode("Entities");

            XmlNode tmpNode = docRoot.SelectSingleNode("Entity[@name='" + entityName + "']");

            if (tmpNode == null)
            {
                XmlElement newElement = _isvConfigXml.CreateElement("Entity");
                newElement.SetAttribute("name", entityName);
                docRoot.AppendChild(newElement);

                tmpNode = docRoot.SelectSingleNode("Entity[@name='" + entityName + "']");
            }

            return tmpNode;
        }

        #endregion

        #endregion

        #region InitializeService
        private void InitializeService(string orgName, string serviceUrl)
        {
            token.AuthenticationType = 0;
            token.OrganizationName = orgName;

            service.Url = serviceUrl + "/2007/CrmService.asmx?uniquename=" + orgName;
            service.CrmAuthenticationTokenValue = token;
            service.UseDefaultCredentials = true;
            LoadIsvConfig();
        }

        private void InitializeService(string orgName, string serviceUrl, ICredentials credentials)
        {
            token.AuthenticationType = 0;
            token.OrganizationName = orgName;

            service.Url = serviceUrl + "/2007/CrmService.asmx?uniquename=" + orgName;
            service.CrmAuthenticationTokenValue = token;
            service.Credentials = credentials;
            LoadIsvConfig();
        }

        private void InitializeService(string orgName, string serviceUrl, string username, string password, string domain)
        {
            System.Net.NetworkCredential cred = new System.Net.NetworkCredential(username, password, domain);

            token.AuthenticationType = 0;
            token.OrganizationName = orgName;

            service.Url = serviceUrl + "/2007/CrmService.asmx?uniquename=" + orgName;
            service.CrmAuthenticationTokenValue = token;
            service.Credentials = cred;
            LoadIsvConfig();
        }
        #endregion

        #region LoadIsvConfig
        private void LoadIsvConfig()
        {
            try
            {
                organization org = null;

                ConditionExpression ce = new ConditionExpression();
                ce.AttributeName = "name";
                ce.Operator = ConditionOperator.Equal;
                ce.Values = new object[] { token.OrganizationName };

                FilterExpression fe = new FilterExpression();
                fe.AddCondition(ce);
                fe.FilterOperator = LogicalOperator.And;

                ColumnSet cols = new ColumnSet();
                cols.AddColumn("organizationid");

                QueryExpression qe = new QueryExpression();
                qe.Criteria = fe;
                qe.EntityName = EntityName.organization.ToString();
                qe.ColumnSet = cols;

                BusinessEntityCollection res = (BusinessEntityCollection)service.RetrieveMultiple(qe);
                if (res.BusinessEntities.Count > 0)
                    org = (organization)res.BusinessEntities[0];

                if (org != null)
                {
                    ce = new ConditionExpression();
                    ce.AttributeName = "organizationid";
                    ce.Operator = ConditionOperator.Equal;
                    ce.Values = new object[] { org.organizationid.Value };

                    fe = new FilterExpression();
                    fe.AddCondition(ce);
                    fe.FilterOperator = LogicalOperator.And;

                    qe = new QueryExpression();
                    qe.Criteria = fe;
                    qe.EntityName = EntityName.isvconfig.ToString();
                    qe.ColumnSet = new AllColumns();

                    res = (BusinessEntityCollection)service.RetrieveMultiple(qe);
                    if (res.BusinessEntities.Count > 0)
                    {
                        isvConf = (isvconfig)res.BusinessEntities[0];
                        _importExportXml.LoadXml(isvConf.configxml);
                        _isvConfigXml = new XmlDocument();
                        _isvConfigXml = _importExportXml;

                        this.GetConfigurationNode();
                    }
                }
            }
            catch (System.Web.Services.Protocols.SoapException err)
            {
                ErrorMessages.Add(err.Detail.InnerText + "\n" + err.StackTrace);
            }
            catch (Exception err)
            {
                ErrorMessages.Add(err.Message + "\n" + err.StackTrace);
            }
        }
        #endregion

        #region SaveIsvConfig
        public void SaveIsvConfig()
        {
            isvConf.configxml = _isvConfigXml.OuterXml;
            service.Update(isvConf);
            PublishXmlRequest pubReq = new PublishXmlRequest();
            pubReq.ParameterXml = @"<configuration></configuration>";
            service.Execute(pubReq);
        }
        #endregion
    }
    #endregion

    #region SiteMapCustomizer
    public class SiteMapCustomizer
    {
        // Fields
        private CrmService service = new CrmService();
        private CrmAuthenticationToken token = new CrmAuthenticationToken();
        public ArrayList ErrorMessages = new ArrayList();
        private XmlDocument _siteMapXml = new XmlDocument();

        #region Constructors
        //public SiteMapCustomizer()
        //{
        //    try
        //    {
        //        string url = (string)Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\MSCRM").GetValue("ServerUrl", string.Empty);
        //        CrmDiscoveryService dService = new CrmDiscoveryService();
        //        dService.UseDefaultCredentials = true;
        //        dService.Url = url + "/2007/AD/CrmDiscoveryService.asmx";
        //        RetrieveOrganizationsRequest req = new RetrieveOrganizationsRequest();
        //        RetrieveOrganizationsResponse resp = (RetrieveOrganizationsResponse)dService.Execute(req);
        //        foreach (OrganizationDetail orgDet in resp.OrganizationDetails)
        //        {
        //            InitializeService(orgDet.OrganizationName, url);
        //            break;
        //        }
        //    }
        //    catch (System.Web.Services.Protocols.SoapException err)
        //    {
        //        ErrorMessages.Add(err.Message + "\n" + err.Detail.InnerText);
        //    }
        //    catch (Exception err)
        //    {
        //        ErrorMessages.Add(err.Message + "\n" + err.StackTrace);
        //    }
        //}

        public SiteMapCustomizer(string orgName)
        {
            try
            {
                string url = (string)Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\MSCRM").GetValue("ServerUrl", string.Empty);
                InitializeService(orgName, url);
            }
            catch (System.Web.Services.Protocols.SoapException err)
            {
                ErrorMessages.Add(err.Message + "\n" + err.Detail.InnerText);
            }
            catch (Exception err)
            {
                ErrorMessages.Add(err.Message + "\n" + err.StackTrace);
            }
        }

        public SiteMapCustomizer(string orgName, string serverUrl, int port)
        {
            InitializeService(orgName, serverUrl + (serverUrl.EndsWith("/") ? "mscrmservices" : "/mscrmservices"));
        }

        public SiteMapCustomizer(string orgName, string serverUrl, int port, string username, string password, string domain)
        {
            InitializeService(orgName, serverUrl + (serverUrl.EndsWith("/") ? "mscrmservices" : "/mscrmservices"), username, password, domain);
        }

        #endregion

        #region Property

        /// <summary>
        /// The current Site Map XML
        /// </summary>
        public XmlDocument Xml
        {
            get
            {
                return _siteMapXml;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Adds an area element node to the XML document
        /// </summary>
        /// <param name="area">Represents an area element node</param>
        public void AddArea(Area area)
        {
            if (IsValid(area))
            {
                // retrieve the XPaths
                string siteMapXPath = GetSiteMapXPath();
                string areaXPath = GetAreaXPath(area.Id);

                // retrieve the element nodes
                XmlElement siteMapElement = _siteMapXml.SelectSingleNode(siteMapXPath) as XmlElement;
                XmlElement areaElement = _siteMapXml.SelectSingleNode(areaXPath) as XmlElement;

                // assume that the sitemap element node exists
                if (siteMapElement == null)
                {
                    throw new NotSupportedException("SiteMap node must exist.");
                }

                if (areaElement == null)
                {
                    // create an element node
                    areaElement = _siteMapXml.CreateElement("Area");

                    #region add attributes
                    if (area.Description != null && area.Description.Length != 0)
                    {
                        areaElement.SetAttribute("Description", area.Description);
                    }
                    if (area.Icon != null && area.Icon.Length != 0)
                    {
                        areaElement.SetAttribute("Icon", area.Icon);
                    }
                    areaElement.SetAttribute("Id", area.Id);
                    if (area.License != LicenseTypes.None)
                    {
                        areaElement.SetAttribute("License", ConvertFlagToString(area.License));
                    }
                    if (area.ShowGroups != ShowGroupsType.None)
                    {
                        areaElement.SetAttribute("ShowGroups", Utility.GetDescriptionAttribute(area.ShowGroups));
                    }
                    areaElement.SetAttribute("Title", area.Title);
                    if (area.Url != null && area.Url.Length != 0)
                    {
                        areaElement.SetAttribute("Url", area.Url);
                    }
                    #endregion

                    // append the area
                    siteMapElement.AppendChild(areaElement);
                }
            }
        }

        /// <summary>
        /// Adds a group element node to the XML document
        /// </summary>
        /// <param name="areaId">Unique Id attribute of the area element node</param>
        /// <param name="group">Represents a group element node</param>
        public void AddGroup(string areaId, Group group)
        {
            if (IsValid(group))
            {
                // retrieve the XPaths
                string areaXPath = GetAreaXPath(areaId);
                string groupXPath = GetGroupXPath(areaId, group.Id);

                // retrieve the element nodes
                XmlElement areaElement = _siteMapXml.SelectSingleNode(areaXPath) as XmlElement;
                XmlElement groupElement = _siteMapXml.SelectSingleNode(groupXPath) as XmlElement;

                // if the area element node does not exist, create it
                if (areaElement == null)
                {
                    // create an area struct with minimal values
                    Area area = new Area();
                    area.Id = areaId;
                    area.Title = areaId;

                    // add the area
                    AddArea(area);

                    // re-retrieve the element node
                    areaElement = _siteMapXml.SelectSingleNode(areaXPath) as XmlElement;
                }

                if (groupElement == null)
                {
                    // create an element node
                    groupElement = _siteMapXml.CreateElement("Group");

                    #region add attributes
                    if (group.Description != null && group.Description.Length != 0)
                    {
                        groupElement.SetAttribute("Description", group.Description);
                    }
                    if (group.Icon != null && group.Icon.Length != 0)
                    {
                        groupElement.SetAttribute("Icon", group.Icon);
                    }
                    groupElement.SetAttribute("Id", group.Id);
                    if (group.IsProfile != IsProfileType.None)
                    {
                        groupElement.SetAttribute("IsProfile", Utility.GetDescriptionAttribute(group.IsProfile));
                    }
                    if (group.License != LicenseTypes.None)
                    {
                        groupElement.SetAttribute("License", ConvertFlagToString(group.License));
                    }
                    groupElement.SetAttribute("Title", group.Title);
                    if (group.Url != null && group.Url.Length != 0)
                    {
                        groupElement.SetAttribute("Url", group.Url);
                    }
                    #endregion

                    // append the group
                    areaElement.AppendChild(groupElement);
                }
            }
            else
            {
                throw new ArgumentException("Group has invalid values.");
            }
        }

        /// <summary>
        /// Adds a sub-area element node to the XML document
        /// </summary>
        /// <param name="areaId">Unique Id attribute of the area element node</param>
        /// <param name="groupId">Unique Id attribute of the group element node</param>
        /// <param name="subArea">Represents a sub-area element node</param>
        public void AddSubArea(SubArea subArea)
        {
            if (IsValid(subArea))
            {
                // retrieve the XPaths
                string groupXPath = GetGroupXPath(subArea.AreaId, subArea.GroupId);
                string subAreaXPath = GetSubAreaXPath(subArea.AreaId, subArea.GroupId, subArea.Id);

                // retrieve the element nodes
                XmlElement groupElement = _siteMapXml.SelectSingleNode(groupXPath) as XmlElement;
                XmlElement subAreaElement = _siteMapXml.SelectSingleNode(subAreaXPath) as XmlElement;

                // if the group element node does not exist, create it
                if (groupElement == null)
                {
                    // create a group struct with minimal values
                    Group group = new Group();
                    group.Id = subArea.GroupId;
                    group.Title = subArea.GroupId;

                    // add the group
                    AddGroup(subArea.AreaId, group);

                    // re-retrieve the element node
                    groupElement = _siteMapXml.SelectSingleNode(groupXPath) as XmlElement;
                }

                // if the area element node does not exist, create it
                if (subAreaElement == null)
                {
                    // create an element node
                    subAreaElement = _siteMapXml.CreateElement("SubArea");

                    #region add attributes
                    if (subArea.AvailableOffline != AvailableOfflineType.None)
                    {
                        subAreaElement.SetAttribute("AvailableOffline", Utility.GetDescriptionAttribute(subArea.AvailableOffline));
                    }
                    if (subArea.Client != ClientTypes.None)
                    {
                        subAreaElement.SetAttribute("Client", subArea.Client.ToString(CultureInfo.InvariantCulture));
                    }
                    if (subArea.Description != null && subArea.Description.Length != 0)
                    {
                        subAreaElement.SetAttribute("Description", subArea.Description);
                    }
                    if (subArea.Entity != null && subArea.Entity.Length != 0)
                    {
                        subAreaElement.SetAttribute("Entity", subArea.Entity);
                    }
                    if (subArea.Icon != null && subArea.Icon.Length != 0)
                    {
                        subAreaElement.SetAttribute("Icon", subArea.Icon);
                    }
                    subAreaElement.SetAttribute("Id", subArea.Id);	// IsValid() already checked
                    if (string.IsNullOrEmpty(subArea.Entity)) subAreaElement.SetAttribute("PassParams", "1");
                    if (subArea.License != LicenseTypes.None)
                    {
                        subAreaElement.SetAttribute("License", ConvertFlagToString(subArea.License));
                    }
                    if (subArea.OutlookShortcutIcon != null && subArea.OutlookShortcutIcon.Length != 0)
                    {
                        subAreaElement.SetAttribute("OutlookShortcutIcon", subArea.OutlookShortcutIcon);
                    }
                    if (!string.IsNullOrEmpty(subArea.Title)) subAreaElement.SetAttribute("Title", subArea.Title);	// IsValid() already checked
                    if (subArea.Url != null && subArea.Url.Length != 0)
                    {
                        subAreaElement.SetAttribute("Url", subArea.Url);
                    }
                    #endregion

                    // append the sub-area
                    groupElement.AppendChild(subAreaElement);
                }
            }
            else
            {
                throw new ArgumentException("SubArea has invalid values.");
            }
        }

        // <summary>
        /// Adds a privilege element node to the XML document
        /// </summary>
        /// <param name="areaId">Unique Id attribute of the area element node</param>
        /// <param name="groupId">Unique Id attribute of the group element node</param>
        /// <param name="subArea">Represents a sub-area element node</param>
        /// <param name="privilege">Represents a new PrivilegeNode object</param>
        public void AddPrivilegeNode(string areaId, string groupId, SubArea subArea, PrivilegeNode privilege)
        {
            if (IsValid(privilege))
            {
                // retrieve the XPaths				
                string subAreaXPath = GetSubAreaXPath(areaId, groupId, subArea.Id);
                string privilegeXPath = GetPrivilegeXPath(areaId, groupId, subArea.Id, privilege.EntityName);

                // retrieve the subArea element			
                XmlElement subAreaElement = _siteMapXml.SelectSingleNode(subAreaXPath) as XmlElement;

                // if the area element node does not exist, do not add the privilege
                if (subAreaElement != null)
                {
                    XmlElement privilegeElement = _siteMapXml.SelectSingleNode(privilegeXPath) as XmlElement;

                    // if the privilege does not already exist, add it
                    if (privilegeElement == null)
                    {
                        privilegeElement = _siteMapXml.CreateElement("Privilege");
                        privilegeElement.SetAttribute("Entity", privilege.EntityName);
                        privilegeElement.SetAttribute("Privilege", ConvertFlagToString(privilege.Privileges));

                        // append the privilege to the subarea
                        subAreaElement.AppendChild(privilegeElement);
                    }
                }
                else
                {
                    throw new ArgumentException("SubArea does not exist.");
                }
            }
            else
            {
                throw new ArgumentException("Privilege has invalid values.");
            }
        }

        /// <summary>
        /// Removes an area element node to the XML document
        /// </summary>
        /// <param name="areaId">Unique Id attribute of the area element node</param>
        public void RemoveArea(string areaId)
        {
            // get XPaths
            string siteMapXPath = GetSiteMapXPath();
            string areaXPath = GetAreaXPath(areaId);

            // retrieve the XML element nodes
            XmlElement siteMapElement = _siteMapXml.SelectSingleNode(siteMapXPath) as XmlElement;
            XmlElement areaElement = _siteMapXml.SelectSingleNode(areaXPath) as XmlElement;

            if (siteMapElement != null && areaElement != null)
            {
                siteMapElement.RemoveChild(areaElement);
            }
        }

        /// <summary>
        /// Removes a group element node to the XML document
        /// </summary>
        /// <param name="areaId">Unique Id attribute of the area element node</param>
        /// <param name="groupId">Unique Id attribute of the group element node</param>
        public void RemoveGroup(string areaId, string groupId)
        {
            // get XPaths
            string areaXPath = GetAreaXPath(areaId);
            string groupXPath = GetGroupXPath(areaId, groupId);

            // retrieve the XML element nodes
            XmlElement areaElement = _siteMapXml.SelectSingleNode(areaXPath) as XmlElement;
            XmlElement groupElement = _siteMapXml.SelectSingleNode(groupXPath) as XmlElement;

            if (areaElement != null && groupElement != null)
            {
                areaElement.RemoveChild(groupElement);
            }
        }

        /// <summary>
        /// Removes a sub-area element node to the XML document
        /// </summary>
        /// <param name="areaId">Unique Id attribute of the area element node</param>
        /// <param name="groupId">Unique Id attribute of the group element node</param>
        /// <param name="subAreaId">Unique Id attribute of the sub-area element node</param>
        public void RemoveSubArea(string areaId, string groupId, string subAreaId)
        {
            // get XPaths
            string groupXPath = GetGroupXPath(areaId, groupId);
            string subAreaXPath = GetSubAreaXPath(areaId, groupId, subAreaId);

            // retrieve the XML element nodes
            XmlElement groupElement = _siteMapXml.SelectSingleNode(groupXPath) as XmlElement;
            XmlElement subAreaElement = _siteMapXml.SelectSingleNode(subAreaXPath) as XmlElement;

            if (groupElement != null && subAreaElement != null)
            {
                groupElement.RemoveChild(subAreaElement);
            }
        }

        #endregion

        #region Methods: Helper

        /// <summary>
        /// Gets the XPath to the sitemap element node
        /// </summary>
        /// <returns>Returns the XPath as a string</returns>
        private string GetSiteMapXPath()
        {
            return "//ImportExportXml/SiteMap/SiteMap";
        }

        /// <summary>
        /// Gets the XPath to the area element node
        /// </summary>
        /// <param name="areaId">Unique Id attribute of the area element node</param>
        /// <returns>Returns the XPath as a string</returns>
        private string GetAreaXPath(string areaId)
        {
            // check that the area Id is alpha-numeric
            if (Utility.IsAlphaNumeric(areaId))
            {
                string siteMapXPath = GetSiteMapXPath();
                return string.Format("{0}/Area[@Id=\"{1}\"]", siteMapXPath, areaId);
            }
            else
            {
                throw new ArgumentException("Invalid Id.");
            }
        }

        /// <summary>
        /// Gets the XPath to the group element node
        /// </summary>
        /// <param name="areaId">Unique Id attribute of the area element node</param>
        /// <param name="groupId">Unique Id attribute of the group element node</param>
        /// <returns>Returns the XPath as a string</returns>
        private string GetGroupXPath(string areaId, string groupId)
        {
            // check that the group Id is alpha-numeric
            if (Utility.IsAlphaNumeric(groupId))
            {
                string areaXPath = GetAreaXPath(areaId);
                return string.Format("{0}/Group[@Id=\"{1}\"]", areaXPath, groupId);
            }
            else
            {
                throw new ArgumentException("Invalid Id.");
            }
        }

        /// <summary>
        /// Gets the XPath to the subArea element node
        /// </summary>
        /// <param name="areaId">Unique Id attribute of the area element node</param>
        /// <param name="groupId">Unique Id attribute of the group element node</param>
        /// <param name="subAreaId">Unique Id attribute of the sub-area element node</param>
        /// <returns>Returns the XPath as a string</returns>
        private string GetSubAreaXPath(string areaId, string groupId, string subAreaId)
        {
            // check that the sub-area Id is alpha-numeric
            if (Utility.IsAlphaNumeric(subAreaId))
            {
                string groupXPath = GetGroupXPath(areaId, groupId);
                return string.Format("{0}/SubArea[@Id=\"{1}\"]", groupXPath, subAreaId);
            }
            else
            {
                throw new ArgumentException("Invalid Id.");
            }
        }

        /// <summary>
        /// Gets the XPath to the privilege element node
        /// </summary>
        /// <param name="areaId">Unique Id attribute of the area element node</param>
        /// <param name="groupId">Unique Id attribute of the group element node</param>
        /// <param name="subAreaId">Unique Id attribute of the sub-area element node</param>
        /// <param name="entityName">Name of the entity that the privilege is checking for</param>
        /// <returns>Returns the XPath as a string</returns>
        private string GetPrivilegeXPath(string areaId, string groupId, string subAreaId, string entityName)
        {
            // check that the entityName is alpha-numeric
            if (Utility.IsAlphaNumeric(entityName))
            {
                string subAreaXPath = GetSubAreaXPath(areaId, groupId, subAreaId);
                return string.Format("{0}/Privilege[@Entity=\"{1}\"]", subAreaXPath, entityName);
            }
            else
            {
                throw new ArgumentException("Invalid EntityName.");
            }
        }

        /// <summary>
        /// Checks to make sure the Area struct is valid
        /// </summary>
        /// <param name="area">Represents an area element node</param>
        /// <returns>Returns true if the struct is valid, otherwise false</returns>
        private bool IsValid(Area area)
        {
            // if there is no Id or no Title, the area is not valid
            if (area.Id == null || area.Id.Length == 0 ||
                area.Title == null || area.Title.Length == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// Checks to make sure the Group struct is valid
        /// </summary>
        /// <param name="group">Represents a group element node</param>
        /// <returns>Returns true if the struct is valid, otherwise false</returns>
        private bool IsValid(Group group)
        {
            // if there is no Id or no Title, the group is not valid
            if (group.Id == null || group.Id.Length == 0 ||
                group.Title == null || group.Title.Length == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// Checks to make sure the SubArea struct is valid
        /// </summary>
        /// <param name="subArea">Represents a sub-area element node</param>
        /// <returns>Returns true if the struct is valid, otherwise false</returns>
        private bool IsValid(SubArea subArea)
        {
            // if there is no Id or no Title, the sub-area is not valid
            if (subArea.Id == null || subArea.Id.Length == 0 ||
                subArea.Title == null || subArea.Title.Length == 0)
            {
                // if there is no client type or if it's only web, the setting cannot be available offline
                if (subArea.Client == ClientTypes.Web || subArea.Client == ClientTypes.None)
                {
                    if (subArea.AvailableOffline == AvailableOfflineType.True)
                    {
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// Checks to make sure the privilege struct is valid
        /// </summary>
        /// <param name="privilegeNode">Represents a privilege element node</param>
        /// <returns>Returns true if the struct is valid, otherwise false</returns>
        private bool IsValid(PrivilegeNode privilegeNode)
        {
            // if there is no EntityName, the group is not valid
            if (privilegeNode.EntityName == null || privilegeNode.EntityName.Length == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// Converts the client flag enum into a comma separated XML attribute value
        /// </summary>
        /// <param name="client"></param>
        /// <returns>Returns the XML attribute value as a string</returns>
        private string ConvertFlagToString(ClientTypes client)
        {
            if (client == ClientTypes.All)
            {
                return string.Format("{0},{1},{2},{3}",
                    ClientTypes.Outlook.ToString(CultureInfo.InvariantCulture),
                    ClientTypes.OutlookLaptopClient.ToString(CultureInfo.InvariantCulture),
                    ClientTypes.OutlookWorkstationClient.ToString(CultureInfo.InvariantCulture),
                    ClientTypes.Web.ToString(CultureInfo.InvariantCulture));
            }
            else if (client == ClientTypes.None)
            {
                return string.Empty;
            }
            else
            {
                StringBuilder output = new StringBuilder();

                if ((client & ClientTypes.Outlook) == ClientTypes.Outlook)
                {
                    output.Append(ClientTypes.Outlook.ToString(CultureInfo.InvariantCulture));
                }
                if ((client & ClientTypes.OutlookLaptopClient) == ClientTypes.OutlookLaptopClient)
                {
                    if (output.Length != 0)
                    {
                        output.Append(',');
                    }
                    output.Append(ClientTypes.OutlookLaptopClient.ToString(CultureInfo.InvariantCulture));
                }
                if ((client & ClientTypes.OutlookWorkstationClient) == ClientTypes.OutlookWorkstationClient)
                {
                    if (output.Length != 0)
                    {
                        output.Append(',');
                    }
                    output.Append(ClientTypes.OutlookWorkstationClient.ToString(CultureInfo.InvariantCulture));
                }
                if ((client & ClientTypes.Web) == ClientTypes.Web)
                {
                    if (output.Length != 0)
                    {
                        output.Append(',');
                    }
                    output.Append(ClientTypes.Web.ToString(CultureInfo.InvariantCulture));
                }

                return output.ToString();
            }
        }

        /// <summary>
        /// Converts the license flag enum into a comma separated XML attribute value 
        /// </summary>
        /// <param name="license"></param>
        /// <returns>Returns the XML attribute value as a string</returns>
        private string ConvertFlagToString(LicenseTypes license)
        {
            if (license == LicenseTypes.All)
            {
                return string.Format("{0},{1}",
                    LicenseTypes.Professional.ToString(CultureInfo.InvariantCulture),
                    LicenseTypes.SmallBusiness.ToString(CultureInfo.InvariantCulture));
            }
            else if (license == LicenseTypes.None)
            {
                return string.Empty;
            }
            else
            {
                StringBuilder output = new StringBuilder();

                if ((license & LicenseTypes.Professional) == LicenseTypes.Professional)
                {
                    output.Append(LicenseTypes.Professional.ToString(CultureInfo.InvariantCulture));
                }
                if ((license & LicenseTypes.SmallBusiness) == LicenseTypes.SmallBusiness)
                {
                    if (output.Length != 0)
                    {
                        output.Append(',');
                    }
                    output.Append(LicenseTypes.SmallBusiness.ToString(CultureInfo.InvariantCulture));
                }

                return output.ToString();
            }
        }

        /// <summary>
        /// Converts the privilege flag enum into a comma separated XML attribute value
        /// </summary>
        /// <param name="privilege"></param>
        /// <returns>Returns the XML attribute value as a string</returns>
        private string ConvertFlagToString(PrivilegeTypes privilege)
        {
            if (privilege == PrivilegeTypes.All)
            {
                return string.Format("{0},{1},{2},{3},{4},{5},{6},{7}",
                    PrivilegeTypes.Append.ToString(CultureInfo.InvariantCulture),
                    PrivilegeTypes.AppendTo.ToString(CultureInfo.InvariantCulture),
                    PrivilegeTypes.Assign.ToString(CultureInfo.InvariantCulture),
                    PrivilegeTypes.Create.ToString(CultureInfo.InvariantCulture),
                    PrivilegeTypes.Delete.ToString(CultureInfo.InvariantCulture),
                    PrivilegeTypes.Read.ToString(CultureInfo.InvariantCulture),
                    PrivilegeTypes.Share.ToString(CultureInfo.InvariantCulture),
                    PrivilegeTypes.Write.ToString(CultureInfo.InvariantCulture));
            }
            else if (privilege == PrivilegeTypes.None)
            {
                return string.Empty;
            }
            else
            {
                StringBuilder output = new StringBuilder();

                if ((privilege & PrivilegeTypes.Append) == PrivilegeTypes.Append)
                {
                    output.Append(PrivilegeTypes.Append.ToString(CultureInfo.InvariantCulture));
                }
                if ((privilege & PrivilegeTypes.AppendTo) == PrivilegeTypes.AppendTo)
                {
                    if (output.Length != 0)
                    {
                        output.Append(',');
                    }
                    output.Append(PrivilegeTypes.AppendTo.ToString(CultureInfo.InvariantCulture));
                }
                if ((privilege & PrivilegeTypes.Assign) == PrivilegeTypes.Assign)
                {
                    if (output.Length != 0)
                    {
                        output.Append(',');
                    }
                    output.Append(PrivilegeTypes.Assign.ToString(CultureInfo.InvariantCulture));
                }
                if ((privilege & PrivilegeTypes.Create) == PrivilegeTypes.Create)
                {
                    if (output.Length != 0)
                    {
                        output.Append(',');
                    }
                    output.Append(PrivilegeTypes.Create.ToString(CultureInfo.InvariantCulture));
                }
                if ((privilege & PrivilegeTypes.Delete) == PrivilegeTypes.Delete)
                {
                    if (output.Length != 0)
                    {
                        output.Append(',');
                    }
                    output.Append(PrivilegeTypes.Delete.ToString(CultureInfo.InvariantCulture));
                }
                if ((privilege & PrivilegeTypes.Read) == PrivilegeTypes.Read)
                {
                    if (output.Length != 0)
                    {
                        output.Append(',');
                    }
                    output.Append(PrivilegeTypes.Read.ToString(CultureInfo.InvariantCulture));
                }
                if ((privilege & PrivilegeTypes.Share) == PrivilegeTypes.Share)
                {
                    if (output.Length != 0)
                    {
                        output.Append(',');
                    }
                    output.Append(PrivilegeTypes.Share.ToString(CultureInfo.InvariantCulture));
                }
                if ((privilege & PrivilegeTypes.Write) == PrivilegeTypes.Write)
                {
                    if (output.Length != 0)
                    {
                        output.Append(',');
                    }
                    output.Append(PrivilegeTypes.Write.ToString(CultureInfo.InvariantCulture));
                }

                return output.ToString();
            }
        }

        #endregion

        #region InitializeService
        private void InitializeService(string orgName, string serviceUrl)
        {
            token.AuthenticationType = 0;
            token.OrganizationName = orgName;

            service.Url = serviceUrl + "/2007/CrmService.asmx";
            service.CrmAuthenticationTokenValue = token;
            service.UseDefaultCredentials = true;

            GetSiteMapXML();
        }

        private void InitializeService(string orgName, string serviceUrl, string username, string password, string domain)
        {
            System.Net.NetworkCredential cred = new System.Net.NetworkCredential(username, password, domain);

            token.AuthenticationType = 0;
            token.OrganizationName = orgName;

            service.Url = serviceUrl + "/2007/CrmService.asmx";
            service.CrmAuthenticationTokenValue = token;
            service.Credentials = cred;

            GetSiteMapXML();
        }
        #endregion

        #region GetSiteMapXML
        public XmlDocument GetSiteMapXML()
        {
            ExportXmlRequest export = new ExportXmlRequest();
            export.ParameterXml = @"<importexportxml>
            <entities></entities>
            <nodes>  
            <node>sitemap</node> 
            </nodes>
            <securityroles></securityroles>
            <settings></settings>
            <workflows></workflows>
            </importexportxml>";

            ExportXmlResponse entities = (ExportXmlResponse)service.Execute(export);
            _siteMapXml.LoadXml(entities.ExportXml);
            _siteMapXml.PreserveWhitespace = true;
            return _siteMapXml;
        }
        #endregion

        #region SaveSiteMapXML
        public void SaveSiteMapXML()
        {
            ImportXmlRequest import = new ImportXmlRequest();
            import.ParameterXml = @"<importexportxml>
            <entities></entities>
            <nodes>  
            <node>sitemap</node> 
            </nodes>
            <securityroles></securityroles>
            <settings></settings>
            <workflows></workflows>
            </importexportxml>";
            import.CustomizationXml = _siteMapXml.OuterXml;
            service.Execute(import);
        }
        #endregion
    }
    #endregion


    #region ToolBarButton
    /// <summary>
    /// Describes a Menu Item that may appear in a menu on the global menu bar, entity menu bar or grid actions menu
    /// </summary>
    public struct ToolBarButton
    {
        private string _title;
        private string _toolTip;
        private string _iconUrl;
        private string _url;
        private string _winParams;
        private string _jScript;
        private char _accessKey;
        private ClientTypes _clients;
        private WindowMode _winMode;
        private ConfigurationBoolean _passParams;
        private ConfigurationBoolean _availableOffline;
        private ConfigurationBoolean _validForCreate;
        private ConfigurationBoolean _validForUpdate;


        /// <summary>
        /// The title of the menu item
        /// </summary>
        public string Title
        {
            get
            {
                return _title;
            }

            set
            {
                _title = value;
            }
        }

        /// <summary>
        /// The tool tip text that will display when the user moves the mouse to the button
        /// </summary>
        public string ToolTip
        {
            get
            {
                return _toolTip;
            }

            set
            {
                _toolTip = value;
            }
        }

        /// <summary>
        /// The Windows "Hot key" that will activate this item.  Note this character must exist as a character
        /// within the title.
        /// </summary>
        public char AccessHotKey
        {
            get
            {
                return _accessKey;
            }

            set
            {
                _accessKey = value;
            }
        }

        /// <summary>
        /// The URL to the Icon that should be rendered next to the title for this item
        /// </summary>
        public string Icon
        {
            get
            {
                return _iconUrl;
            }

            set
            {
                _iconUrl = value;
            }
        }

        /// <summary>
        /// The URL to load when the user navigates to this item
        /// </summary>
        public string Url
        {
            get
            {
                return _url;
            }

            set
            {
                _url = value;
            }
        }

        /// <summary>
        /// Determines what type of new window should be loaded when the user selects this item
        /// </summary>
        public string JScript
        {
            get
            {
                return _jScript;
            }

            set
            {
                _jScript = value;
            }
        }

        /// <summary>
        /// Determines what type of new window should be loaded when the user selects this item
        /// </summary>
        public WindowMode WindowLaunchMode
        {
            get
            {
                return _winMode;
            }

            set
            {
                _winMode = value;
            }
        }

        /// <summary>
        /// Determines what type of new window should be loaded when the user selects this item
        /// </summary>
        public string WindowParameters
        {
            get
            {
                return _winParams;
            }

            set
            {
                _winParams = value;
            }
        }


        /// <summary>
        /// Determines what Microsoft CRM clients this menu item should be available in
        /// </summary>
        public ClientTypes SupportedClients
        {
            get
            {
                return _clients;
            }

            set
            {
                _clients = value;
            }
        }

        /// <summary>
        /// Indicates if the object type and object ID parameters are to be passed to the URL. 
        /// </summary>
        public ConfigurationBoolean PassParameters
        {
            get
            {
                return _passParams;
            }

            set
            {
                _passParams = value;
            }
        }

        /// <summary>
        /// Set this to true if the menu item does not depend on the user being connected to the organization's network.
        /// </summary>
        public ConfigurationBoolean AvailableOffline
        {
            get
            {
                return _availableOffline;
            }

            set
            {
                _availableOffline = value;
            }
        }

        /// <summary>
        /// Indicates if this menu item should render on the "Create" (New) form of an entity
        /// </summary>
        public ConfigurationBoolean ValidForCreate
        {
            get
            {
                return _validForCreate;
            }

            set
            {
                _validForCreate = value;
            }
        }

        /// <summary>
        /// Indicates if this menu item should render on the "Update" (Existing) form of an entity
        /// </summary>
        public ConfigurationBoolean ValidForUpdate
        {
            get
            {
                return _validForUpdate;
            }

            set
            {
                _validForUpdate = value;
            }
        }
    }
    #endregion

    #region MenuItem
    /// <summary>
    /// Describes a Menu Item that may appear in a menu on the global menu bar, entity menu bar or grid actions menu
    /// </summary>
    public struct MenuItem
    {
        private string _title;
        private string _iconUrl;
        private string _url;
        private string _winParams;
        private string _jScript;
        private char _accessKey;
        private ClientTypes _clients;
        private WindowMode _winMode;
        private ConfigurationBoolean _passParams;
        private ConfigurationBoolean _availableOffline;
        private ConfigurationBoolean _validForCreate;
        private ConfigurationBoolean _validForUpdate;

        /// <summary>
        /// The title of the menu item
        /// </summary>
        public string Title
        {
            get
            {
                return _title;
            }

            set
            {
                _title = value;
            }
        }

        /// <summary>
        /// The Windows "Hot key" that will activate this item.  Note this character must exist as a character
        /// within the title.
        /// </summary>
        public char AccessHotKey
        {
            get
            {
                return _accessKey;
            }

            set
            {
                _accessKey = value;
            }
        }

        /// <summary>
        /// The URL to the Icon that should be rendered next to the title for this item
        /// </summary>
        public string Icon
        {
            get
            {
                return _iconUrl;
            }

            set
            {
                _iconUrl = value;
            }
        }

        /// <summary>
        /// The URL to load when the user navigates to this item
        /// </summary>
        public string Url
        {
            get
            {
                return _url;
            }

            set
            {
                _url = value;
            }
        }

        /// <summary>
        /// Determines what type of new window should be loaded when the user selects this item
        /// </summary>
        public string JScript
        {
            get
            {
                return _jScript;
            }

            set
            {
                _jScript = value;
            }
        }

        /// <summary>
        /// Determines what type of new window should be loaded when the user selects this item
        /// </summary>
        public WindowMode WindowLaunchMode
        {
            get
            {
                return _winMode;
            }

            set
            {
                _winMode = value;
            }
        }

        /// <summary>
        /// Determines what type of new window should be loaded when the user selects this item
        /// </summary>
        public string WindowParameters
        {
            get
            {
                return _winParams;
            }

            set
            {
                _winParams = value;
            }
        }


        /// <summary>
        /// Determines what Microsoft CRM clients this menu item should be available in
        /// </summary>
        public ClientTypes SupportedClients
        {
            get
            {
                return _clients;
            }

            set
            {
                _clients = value;
            }
        }

        /// <summary>
        /// Indicates if the object type and object ID parameters are to be passed to the URL. 
        /// </summary>
        public ConfigurationBoolean PassParameters
        {
            get
            {
                return _passParams;
            }

            set
            {
                _passParams = value;
            }
        }

        /// <summary>
        /// Set this to true if the menu item does not depend on the user being connected to the organization's network.
        /// </summary>
        public ConfigurationBoolean AvailableOffline
        {
            get
            {
                return _availableOffline;
            }

            set
            {
                _availableOffline = value;
            }
        }

        /// <summary>
        /// Indicates if this menu item should render on the "Create" (New) form of an entity
        /// </summary>
        public ConfigurationBoolean ValideForCreate
        {
            get
            {
                return _validForCreate;
            }

            set
            {
                _validForCreate = value;
            }
        }

        /// <summary>
        /// Indicates if this menu item should render on the "Update" (Existing) form of an entity
        /// </summary>
        public ConfigurationBoolean ValidForUpdate
        {
            get
            {
                return _validForUpdate;
            }

            set
            {
                _validForUpdate = value;
            }
        }
    }
    #endregion

    #region IsvLeftNavigationItem
    /// <summary>
    /// Describes an entity form left navigation item
    /// </summary>
    public struct NavigationBarItem
    {
        private string _title;
        private string _iconUrl;
        private string _url;
        private string _id;
        private string _area;

        /// <summary>
        /// The title of the navigation item
        /// </summary>
        public string Title
        {
            get
            {
                return _title;
            }

            set
            {
                _title = value;
            }
        }

        /// <summary>
        /// The URL to the Icon that should be rendered next to the title for this item
        /// </summary>
        public string Icon
        {
            get
            {
                return _iconUrl;
            }

            set
            {
                _iconUrl = value;
            }
        }

        /// <summary>
        /// The URL to load when the user navigates to this item
        /// </summary>
        public string Url
        {
            get
            {
                return _url;
            }

            set
            {
                _url = value;
            }
        }


        /// <summary>
        /// The unique client-side ID of this item
        /// </summary>
        public string Id
        {
            get
            {
                return _id;
            }

            set
            {
                _id = value;
            }
        }
        public string Area
        {
            get
            {
                return _area;
            }

            set
            {
                _area = value;
            }
        }
    }
    #endregion

    #region Utility
    public class Utility
    {
        #region Methods

        /// <summary>
        /// Retrieves the description attribute from an enum value
        /// </summary>
        /// <param name="enumValue">The enum value who's description attribute to retrieve</param>
        /// <returns>Returns the description attribute of an enum value as a string</returns>
        public static string GetDescriptionAttribute(Enum enumValue)
        {
            // retrieves the information on the enum field (as opposed to the enum type itself)
            FieldInfo fieldInfo = enumValue.GetType().GetField(enumValue.ToString());

            // retrieves all custom attributes of type DescriptionAttribute (should only be one)
            DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

            // if there is an attribute, then return it
            if (attributes != null && attributes.Length > 0)
            {
                return attributes[0].Description;
            }
            else
            {
                // throw an error message if there is no DescriptionAttribute on the enum field
                string errorMessage = string.Format("{0} does not have a DescriptionAttribute.", enumValue);
                throw new ArgumentException(errorMessage);
            }
        }

        /// <summary>
        /// Used to check that all the characters are a-z (capital and lower-case), 0-9, or _ (an underscore)
        /// </summary>
        /// <param name="input">String value whose characters are being checked</param>
        /// <returns>Returns true if all the characters of the input string are alpha-numeric (+ underscore), otherwise false</returns>
        public static bool IsAlphaNumeric(string input)
        {
            // set the pattern to check
            string pattern = "^[a-zA-Z0-9_]+$";

            if (Regex.IsMatch(input, pattern))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion
    }
    #endregion

    #region Area
    public struct Area
    {
        #region Fields
        private string _description;
        private string _icon;
        private string _id;
        private LicenseTypes _license;
        private ShowGroupsType _showGroups;
        private string _title;
        private string _url;
        #endregion

        #region Properties
        public string Description
        {
            get
            {
                return _description;
            }
            set
            {
                _description = value;
            }
        }

        public string Icon
        {
            get
            {
                return _icon;
            }
            set
            {
                _icon = value;
            }
        }

        public string Id
        {
            get
            {
                return _id;
            }
            set
            {
                if (Utility.IsAlphaNumeric(value))
                {
                    _id = value;
                }
            }
        }

        public LicenseTypes License
        {
            get
            {
                return _license;
            }
            set
            {
                _license = value;
            }
        }

        public ShowGroupsType ShowGroups
        {
            get
            {
                return _showGroups;
            }
            set
            {
                _showGroups = value;
            }
        }

        public string Title
        {
            get
            {
                return _title;
            }
            set
            {
                _title = value;
            }
        }

        public string Url
        {
            get
            {
                return _url;
            }
            set
            {
                _url = value;
            }
        }
        #endregion
    }
    #endregion

    #region Group
    public struct Group
    {
        #region Fields
        private string _description;
        private string _icon;
        private string _id;
        private IsProfileType _isProfile;
        private LicenseTypes _license;
        private string _title;
        private string _url;
        #endregion

        #region Properties
        public string Description
        {
            get
            {
                return _description;
            }
            set
            {
                _description = value;
            }
        }

        public string Icon
        {
            get
            {
                return _icon;
            }
            set
            {
                _icon = value;
            }
        }

        public string Id
        {
            get
            {
                return _id;
            }
            set
            {
                if (Utility.IsAlphaNumeric(value))
                {
                    _id = value;
                }
            }
        }

        public IsProfileType IsProfile
        {
            get
            {
                return _isProfile;
            }
            set
            {
                _isProfile = value;
            }
        }

        public LicenseTypes License
        {
            get
            {
                return _license;
            }
            set
            {
                _license = value;
            }
        }

        public string Title
        {
            get
            {
                return _title;
            }
            set
            {
                _title = value;
            }
        }

        public string Url
        {
            get
            {
                return _url;
            }
            set
            {
                _url = value;
            }
        }
        #endregion
    }
    #endregion

    #region SubArea
    public struct SubArea
    {
        #region Fields
        private AvailableOfflineType _availableOffline;
        private ClientTypes _client;
        private string _description;
        private string _entity;
        private string _icon;
        private string _id;
        private LicenseTypes _license;
        private string _outlookShortcutIcon;
        private string _title;
        private string _url;
        private string _areaId;
        private string _groupId;
        #endregion

        #region Properties
        public AvailableOfflineType AvailableOffline
        {
            get
            {
                return _availableOffline;
            }
            set
            {
                _availableOffline = value;
            }
        }

        public ClientTypes Client
        {
            get
            {
                return _client;
            }
            set
            {
                _client = value;
            }
        }

        public string Description
        {
            get
            {
                return _description;
            }
            set
            {
                _description = value;
            }
        }
        public string Entity
        {
            get
            {
                return _entity;
            }
            set
            {
                _entity = value;
            }
        }

        public string Icon
        {
            get
            {
                return _icon;
            }
            set
            {
                _icon = value;
            }
        }

        public string Id
        {
            get
            {
                return _id;
            }
            set
            {
                if (Utility.IsAlphaNumeric(value))
                {
                    _id = value;
                }
            }
        }

        public LicenseTypes License
        {
            get
            {
                return _license;
            }
            set
            {
                _license = value;
            }
        }

        public string OutlookShortcutIcon
        {
            get
            {
                return _outlookShortcutIcon;
            }
            set
            {
                _outlookShortcutIcon = value;
            }
        }

        public string Title
        {
            get
            {
                return _title;
            }
            set
            {
                _title = value;
            }
        }

        public string Url
        {
            get
            {
                return _url;
            }
            set
            {
                _url = value;
            }
        }

        public string AreaId
        {
            get
            {
                return _areaId;
            }
            set
            {
                _areaId = value;
            }
        }

        public string GroupId
        {
            get
            {
                return _groupId;
            }
            set
            {
                _groupId = value;
            }
        }
        #endregion
    }
    #endregion

    #region PrivelegeNode
    public struct PrivilegeNode
    {
        #region Fields

        private string _entityName;
        private PrivilegeTypes _privileges;

        #endregion

        #region Properties

        /// <summary>
        /// This is the entity that the privileges will be checked against
        /// </summary>
        public string EntityName
        {
            get
            {
                return _entityName;
            }
            set
            {
                _entityName = value;
            }
        }

        /// <summary>
        /// These are the privileges that will be applied to the entity
        /// The user must have these privileges in order to access the sub area
        /// </summary>
        public PrivilegeTypes Privileges
        {
            get
            {
                return _privileges;
            }
            set
            {
                _privileges = value;
            }
        }

        #endregion
    }
    #endregion

}